package com.rockcent.wechat.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.rockcent.common.common.BusinessException;
import com.rockcent.common.common.JsonResult;
import com.rockcent.common.controller.BaseController;
import com.rockcent.helper.MallFundHelper;
import com.rockcent.mall.lib.domain.*;
import com.rockcent.mall.lib.domain.enumclass.*;
import com.rockcent.mall.lib.dto.MallOrderPaymentDto;
import com.rockcent.mall.lib.helper.*;
import com.rockcent.mall.lib.repository.*;
import com.rockcent.service.PaymentService;
import com.rockcent.wechat.Config;
import com.rockcent.wechat.Prop;
import com.rockcent.wechat.Routes;
import com.rockcent.wechat.helper.*;
import com.rockcent.wechat.payment.RXStreamTransformer;
import com.rockcent.wechat.payment.WechatPaymentAttach;
import com.rockcent.wechat.payment.WxMpPayCallBackReturnResult;
import com.thoughtworks.xstream.XStream;
import me.chanjar.weixin.common.util.xml.XStreamInitializer;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.pay.WxPayJsSDKCallback;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.NoTransactionException;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.List;

/**
 * Created by HK on 16/10/29.
 */
@Controller
@RequestMapping(value = Routes.API_PREFIX)
public class PaymentNotifyController extends BaseController {

    private final Logger log = LoggerFactory.getLogger(PaymentNotifyController.class);

    @Autowired
    private MallOrderRepository mallOrderRepository;
    @Autowired
    private WechatHelper wechatHelper;
    @Autowired
    private MallOrderPaymentRepository mallOrderPaymentRepository;
    @Autowired
    private MallOrderPaymentFlowRepository mallOrderPaymentFlowRepository;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private MerchantOrderRepository merchantOrderRepository;
    @Autowired
    private CustomerFundRepository customerFundRepository;
    @Autowired
    private MallFundRepository mallFundRepository;
    @Autowired
    private CustomerFundFlowRepository customerFundFlowRepository;
    @Autowired
    private MallFundFlowRepository mallFundFlowRepository;
    @Autowired
    private Prop prop;
    @Autowired
    private MallCashCouponFlowRepository mallCashCouponFlowRepository;
    @Autowired
    private PaymentService paymentService;
    @Autowired
    private CustomerRepository customerRepository;
    @Autowired
    private WxAppMsgTemplateHelper wxAppMsgTemplateHelper;
    @Autowired
    private MiniAppConfigRepository miniAppConfigRepository;

    /**
     * 微信支付回调通知
     *
     * 微信回调正确请求的数据 : WxMpPayCallback [return_code=SUCCESS,
     *                                          return_msg=null,
     *                                          appid=wxce62f97f15d07734,
     *                                          mch_id=1296033001,
     *                                          device_info=null,
     *                                          nonce_str=1464506189025,
     *                                          sign=46A5C769EF1DD4A0018C951D13AB2D5D,
     *                                          result_code=SUCCESS,
     *                                          err_code=null,
     *                                          err_code_des=null,
     *                                          openid=oU83_souEPtkEAZHq8DMhM-SR3Lg,
     *                                          is_subscribe=Y,
     *                                          trade_type=JSAPI,
     *                                          bank_type=CMB_DEBIT,
     *                                          total_fee=1,
     *                                          fee_type=CNY,
     *                                          cash_fee=1,
     *                                          cash_fee_type=null,
     *                                          coupon_fee=null,
     *                                          coupon_count=null,
     *                                          coupon_batch_id_$n=null,
     *                                          coupon_id_$n=null,
     *                                          coupon_fee_$n=null,
     *                                          transaction_id=4003512001201605296470458116,
     *                                          out_trade_no=20160529151529213_990649,
     *                                          attach=null,
     *                                          time_end=20160529151640]
     * 成功返回数据: <xml>
     *              <return_code><![CDATA[SUCCESS]]></return_code>
     *              <return_msg><![CDATA[OK]]></return_msg>
     *          </xml>
     */
    @Transactional
    @RequestMapping(value = "/payment_notify/wechat", method = RequestMethod.POST)
    @ResponseBody
    public void paymentNotify(HttpServletRequest request, HttpServletResponse response) {
        WxMpPayCallBackReturnResult outCallBack = new WxMpPayCallBackReturnResult();
        WxPayJsSDKCallback callback = null;
        StringBuffer inputData = new StringBuffer();
        try {
            List<String> strings = IOUtils.readLines(request.getInputStream());
            strings.forEach(s -> inputData.append(s));
//            log.info("======================>notify inputData : {}", inputData.toString());

            XStream xstream = XStreamInitializer.getInstance();
            xstream.alias("xml", WxPayJsSDKCallback.class);
            WxPayJsSDKCallback wxMpCallback = (WxPayJsSDKCallback) xstream.fromXML(inputData.toString());

//            log.info("======================>WxMpPayCallback  {}", objectMapper.writeValueAsString(wxMpCallback));

            if (wxMpCallback == null || StringUtils.isBlank(wxMpCallback.getAttach())) {
//                log.warn("======================>微信支付恶意回调请求, data : {}", inputData.toString());
                return;
            }

            WechatPaymentAttach wechatPaymentAttach = objectMapper.readValue(wxMpCallback.getAttach(), WechatPaymentAttach.class);
            String domain = wechatPaymentAttach.getDomain();
//            log.info("======================>wechatPaymentAttach wechatPaymentAttach.domain : {} , wechatPaymentAttach.code : {}", wechatPaymentAttach.getDomain(), wechatPaymentAttach.getCode());

            if (wechatPaymentAttach == null) {
//                log.warn("======================>微信支付恶意回调请求, data : {}", inputData.toString());
                return;
            }

            String value = stringRedisTemplate.opsForValue().get("wechat_payment_code_" + wechatPaymentAttach.getCode());
//            log.info("======================>redis code  {}", value);

            // 把安全码放入redis 做回调请求校验
            if (!stringRedisTemplate.hasKey("wechat_payment_code_" + wechatPaymentAttach.getCode())) {
//                log.warn("======================>微信支付恶意回调请求, data : {}", inputData.toString());
                return;
            }
            // 获取对应域名下的wxMpService
            WxMpService wxMpService = wechatHelper.getWxMpService(wechatPaymentAttach.getDomain());

            callback = wxMpService.getPayService().getJSSDKCallbackData(inputData.toString());

            callback.getTransaction_id();
//            log.info("======================>paymentNotify result {}", callback);

            MallOrderPaymentFlow notifyMallOrderPaymentFlow = mallOrderPaymentFlowRepository.findByFlowNum(callback.getOut_trade_no());
            if (notifyMallOrderPaymentFlow == null) {
                log.error(MallOrderPaymentFlow.REMIND_RECORD_IS_NOT_EXIST,log);;
                return;
            }
            MallOrderPayment mallOrderPayment = mallOrderPaymentRepository.findByPaymentNum(notifyMallOrderPaymentFlow.getPaymentNum());
            if (mallOrderPayment == null) {
//                log.warn("======================>payment {} ", MallOrderPayment.REMIND_RECORD_IS_NOT_EXIST,log);;
                return;
            }
            // 支付成功 回调
            if ("SUCCESS".equals(callback.getResult_code())) {
                if (mallOrderPayment.getStatus() != MallOrderPaymentStatus.WAIT) {
                    log.warn("======================>wechat callback fail , paymentNum : {}", mallOrderPayment.getPaymentNum());
                    return;
                }

                this.fundDoing(mallOrderPayment, callback);
                Long customerId = mallOrderPayment.getCustomerId();
                Customer customer = customerRepository.findOne(customerId);
                String openId =null;
                String customerName = customer.getNickName();
                MallOrder mallOrder = mallOrderRepository.findByOrderNumAndMallId(mallOrderPayment.getMallOrderNum(), mallOrderPayment.getMallId());

                //消息通知
                OrderSource orderSource = notifyMallOrderPaymentFlow.getOrderSource();

                if (orderSource == OrderSource.WXAPP) {
                    openId = customer.getWxappOpenId();
                    String prePayId = notifyMallOrderPaymentFlow.getPrepayId();
                    MiniAppConfig miniAppConfig = miniAppConfigRepository.findByMallId(mallOrder.getMallId());
                    if (miniAppConfig == null) {
                        log.error("小程序配置信息不存在");
                        return;
                    }
                    wxAppMsgTemplateHelper.sendWxAppMsg(domain, openId, customerName, mallOrder, prePayId, miniAppConfig);
                } else if (orderSource == OrderSource.WECHAT) {


                }

                // 当处理成功后,给微信返回成功结果
                outCallBack.setReturnCode("SUCCESS");
                outCallBack.setReturnMsg("OK");
                String resultXml = RXStreamTransformer.toXml(WxMpPayCallBackReturnResult.class, outCallBack);
//                log.info("======================>resultXml {}", resultXml);
                response.getWriter().print(resultXml);

            } else {
                // 支付失败 回调
                log.warn("======================>payment notify {} ", callback.toString());
            }
        } catch (Exception e) {
            // 当如果发生异常数据回滚
            try {
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            } catch (NoTransactionException e1) {
            }
            log.warn("======================>payment notify {} ", callback.toString());
            super.showErrorLog(e, log);
        }
    }


    /**
     * @api {post} /api/v2/payment_notify/wechat_simulation 支付回调 微信模拟
     * @apiGroup payment
     * @apiVersion 1.0.0
     * @apiHeader {String} domain <code>必须参数</code>域名
     * @apiHeader {String} userToken <code>必须参数</code>userToken
     *
     * @apiParam {String} mallOrderPaymentFlowNum <code>必须参数</code>平台订单支付流水号
     * @apiParam {String} password <code>必须参数</code>密码
     *
     *
     * @apiParamExample {json} 微信模拟支付回调:
     * {
     *  "mallOrderPaymentFlowNum":"201611071428076233593233",
     *  "password":123456
     * }
     *
     * @apiSuccessExample {json} Success-Response:
     * {
     *  "code": "1",
     *  "desc": "成功"
     * }
     *
     */
    @Transactional
    @RequestMapping(value = "/payment_notify/wechat_simulation", method = {RequestMethod.POST})
    @ResponseBody
    public JsonResult paymentNotifySimulation(@RequestBody MallOrderPaymentDto dto, HttpServletRequest request, HttpServletResponse response) throws IOException {


        JsonResult jsonResult = new JsonResult();

        // 线上环境不能使用
        if (prop.env.equals(Config.PROD) && 1946494628469946724L != dto.getPassword()) {
            jsonResult.remind("生产环境不能使用此接口",log);
            return jsonResult;
        }
        // 模拟测试支付成功
        if (dto.getPassword() == null) {
            jsonResult.remind("支付密码不能为空",log);
            return jsonResult;
        }
        if (dto.getPassword() != 1946494628469946724L) {
            jsonResult.remind("密码错误",log);
            return jsonResult;
        }
        if (StringUtils.isBlank(dto.getMallOrderPaymentFlowNum())) {
            jsonResult.errorParam("paymentFlowNum 不能为空",log);
            return jsonResult;
        }


        MallOrderPaymentFlow notifyMallOrderPaymentFlow = mallOrderPaymentFlowRepository.findByFlowNum(dto.getMallOrderPaymentFlowNum());
        if (notifyMallOrderPaymentFlow == null) {
            log.error(MallOrderPaymentFlow.REMIND_RECORD_IS_NOT_EXIST,log);;
            return jsonResult;
        }
        MallOrderPayment mallOrderPayment = mallOrderPaymentRepository.findByPaymentNum(notifyMallOrderPaymentFlow.getPaymentNum());
        if (mallOrderPayment == null) {
            log.warn("======================>payment {} ", MallOrderPayment.REMIND_RECORD_IS_NOT_EXIST,log);;
            return jsonResult;
        }
        // 支付成功 回调
        if (mallOrderPayment.getStatus() != MallOrderPaymentStatus.WAIT) {
            log.warn("======================>wechat callback fail , paymentNum : {}", mallOrderPayment.getPaymentNum());
            jsonResult.remind("平台订单已回调成功，不能重复回调。",log);
            return jsonResult;
        }
        this.fundDoing(mallOrderPayment, null);
        return jsonResult;
    }


    private void fundDoing(MallOrderPayment mallOrderPayment, WxPayJsSDKCallback callback) {
        mallOrderPayment.setPaymentMethod(PaymentMethod.WECHAT);
        mallOrderPayment.setPaymentTransaction(OrderHelper.SINGLEONE.genMallOrderPaymentFlowNumber());// 模拟一个外部交易号
        if (callback != null) {
            mallOrderPayment.setPaymentTransaction(callback.getTransaction_id());// 模拟一个外部交易号

        }
        mallOrderPayment.setStatus(MallOrderPaymentStatus.SUCCESS);
        mallOrderPaymentRepository.save(mallOrderPayment);

        MallOrder mallOrder = mallOrderRepository.findByOrderNumAndMallId(mallOrderPayment.getMallOrderNum(), mallOrderPayment.getMallId());
        // 添加支付流水
        MallOrderPaymentFlow mallOrderPaymentFlow = new MallOrderPaymentFlow();
        mallOrderPaymentFlow.setFlowNum(OrderHelper.SINGLEONE.genMallOrderPaymentFlowNumber());
        mallOrderPaymentFlow.setPaymentNum(mallOrderPayment.getPaymentNum());
        mallOrderPaymentFlow.setCustomerId(mallOrderPayment.getCustomerId());
        mallOrderPaymentFlow.setMallOrderId(mallOrderPayment.getMallOrderId());
        mallOrderPaymentFlow.setMallOrderNum(mallOrderPayment.getMallOrderNum());
        mallOrderPaymentFlow.setMallId(mallOrderPayment.getMallId());
        mallOrderPaymentFlow.setAmount(mallOrderPayment.getAmount());
        mallOrderPaymentFlow.setStatus(MallOrderPaymentStatus.SUCCESS);
        mallOrderPaymentFlow.setPaymentMethod(PaymentMethod.WECHAT);
        mallOrderPaymentFlowRepository.save(mallOrderPaymentFlow);


        // 修改平台订单状态
        if (mallOrder.getStatus() != MallOrderStatus.WAIT_PAYING) {
            log.warn("======================>wechat callback fail , paymentNum : {}", mallOrderPayment.getPaymentNum());
            throw new BusinessException("wechat callback fail , mallOrder orderStatus : " + mallOrder.getStatus());
        }

        OrderStatusHelper.SINGLETON.mallOrderPayed(mallOrder); //修改平台订单为已支付
        if (callback != null) {
            mallOrder.setPaymentTransaction(callback.getTransaction_id());
        }
        mallOrder.setPaymentMethod(PaymentMethod.WECHAT);
        mallOrderRepository.save(mallOrder);

        List<MerchantOrder> merchantOrderList = merchantOrderRepository.findByMallOrderNum(mallOrder.getOrderNum());
        // 支付成功 修改商家订单
        paymentService.modifyMerchantOrder(mallOrder,merchantOrderList);

        // 用户资金
        Customer customer = customerRepository.findOne(mallOrder.getCustomerId());
        CustomerFund customerFund = customerFundRepository.findOne(customer.getCustomerFundId());
        // 用户资金 充值
        CustomerFundFlow incomeCustomerFundFlow = CustomerFundFlowHelper.SINGLEONE.create(customerFund, mallOrder, mallOrder.getActualAmount(), BigDecimal.ZERO, mallOrder.getActualAmount(), FundTransactionFlow.INCOME, CustomerFundFlowTransactionType.ORDER_RECHARGE, "--微信支付",PaymentMethod.WECHAT);
        customerFund.setAvailableAmount(incomeCustomerFundFlow.getAvailableAmount());
        customerFund.setFrozen(incomeCustomerFundFlow.getFrozen());
        customerFund.setTotalAmount(incomeCustomerFundFlow.getAvailableAmount().add(incomeCustomerFundFlow.getFrozen()));
        customerFundFlowRepository.save(incomeCustomerFundFlow);
        // 用户资金 支出
        CustomerFundFlow expendCustomerFundFlow = CustomerFundFlowHelper.SINGLEONE.create(customerFund, mallOrder, mallOrder.getActualAmount(), BigDecimal.ZERO, mallOrder.getActualAmount(), FundTransactionFlow.EXPEND, CustomerFundFlowTransactionType.SHOP, "--钱包扣除",PaymentMethod.WECHAT);
        customerFund.setAvailableAmount(expendCustomerFundFlow.getAvailableAmount());
        customerFund.setFrozen(expendCustomerFundFlow.getFrozen());
        customerFund.setTotalAmount(expendCustomerFundFlow.getAvailableAmount().add(expendCustomerFundFlow.getFrozen()));
        customerFundFlowRepository.save(expendCustomerFundFlow);

        MallFund mallFund = mallFundRepository.findOne(mallOrder.getMallFundId());

        // 平台冻结资金加 用户微信支付加
        MallFundFlow mallFundFlow = MallFundFlowHelper.SINGLEONE.create(mallFund, mallOrder.getOrderNum(), BigDecimal.ZERO, mallOrder.getActualAmount(), mallOrder.getActualAmount(), FundTransactionFlow.INCOME, MallFundFlowTransactionType.F_PAYMENT_SUCCESS_FROZEN, PaymentMethod.WECHAT, null);
        mallFundFlow = mallFundFlowRepository.save(mallFundFlow);
        mallFundFlow.setParentId(mallFundFlow.getId());
        mallFundFlowRepository.save(mallFundFlow);
        MallFundHelper.SINGLEONE.calculationMallFundAmount(mallFund, mallFundFlow.getFrozen(), mallFundFlow.getAvailableAmount());
        // 使用现金券
        if (mallOrder.getCashCouponId() != null) {
            // 平台现金账户上减
            mallFund.setAvailableCashAmount(mallFund.getAvailableCashAmount().subtract(mallOrder.getCashCouponAmount()));
            // 平台现金账户上减 流水
            MallCashCouponFlow mallCashCouponFlow = new MallCashCouponFlow();
            mallCashCouponFlow.setOperationAmount(mallOrder.getCashCouponAmount());
            mallCashCouponFlow.setTransactionFlow(FundTransactionFlow.EXPEND);
            mallCashCouponFlow.setMallId(mallOrder.getMallId());
            mallCashCouponFlow.setCashCouponId(mallOrder.getCashCouponId());
            mallCashCouponFlowRepository.save(mallCashCouponFlow);

            // 平台冻结资金加 用户现金券加
            MallFundFlow cashCouponMallFundFlow = MallFundFlowHelper.SINGLEONE.create(mallFund, mallOrder.getOrderNum(), BigDecimal.ZERO, mallOrder.getCashCouponAmount(), mallOrder.getCashCouponAmount(), FundTransactionFlow.INCOME, MallFundFlowTransactionType.F_PAYMENT_SUCCESS_FROZEN, PaymentMethod.CASH_COUPON, null,mallFundFlow.getParentId());
            cashCouponMallFundFlow = mallFundFlowRepository.save(cashCouponMallFundFlow);
            MallFundHelper.SINGLEONE.calculationMallFundAmount(mallFund, cashCouponMallFundFlow.getFrozen(), cashCouponMallFundFlow.getAvailableAmount());
        }
        // 平台资金账户
        mallFundRepository.save(mallFund);

        // 当服务订单内包含不可退产品支付成功,马上结算
        paymentService.productServiceSettled(mallOrder, merchantOrderList);

        // 如果购买礼包赠送卡券时，支付成功把卡券赠送到用户
        paymentService.giftGiveCoupon(mallOrder);


    }


}
